package com.lingchen.boot.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class CalendarUtils {

    /**
     * 获得当日零点时间对象
     * 
     * @param type
     * @return
     */
    public static <T extends Date> T getTodayZero(Class<T> type) {
        return getBeforeDayZero(0, type);
    }

    public static <T extends Date> T getYesterdayZero(Class<T> type) {
        return getBeforeDayZero(1, type);
    }

    public static <T extends Date> T getTomorrowZero(Class<T> type) {
        return getBeforeDayZero(-1, type);
    }

    /**
     * 获得几日前零点对象
     * @param d
     * @param type
     * @return
     */
    public static <T extends Date> T getBeforeDayZero(int d, Class<T> type) {
        ZonedDateTime today = LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault());
        if (d != 0) {
            today = today.minusDays(d);
        }
        try {
            long millis = today.toInstant().toEpochMilli();
            Constructor<T> constructor = type.getConstructor(long.class);
            if (constructor != null) {
                T t = constructor.newInstance(millis);
                return t;
            }
        } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        throw new RuntimeException();
    }

    public static <T extends Date> T resetDayZero(long time, Class<T> type) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        try {
            long millis = calendar.getTimeInMillis();
            Constructor<T> constructor = type.getConstructor(long.class);
            if (constructor != null) {
                T t = constructor.newInstance(millis);
                return t;
            }
        } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        throw new RuntimeException();
    }

    public static <T extends Date> T resetDayZero(T date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(date.getTime());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        date.setTime(calendar.getTimeInMillis());
        
        return date;
    }

    public static Date getBeforeDay(Date day, int n) {
        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(day);
        ZonedDateTime datetime = ZonedDateTime.ofInstant(day.toInstant(), ZoneId.systemDefault());
        ZonedDateTime minusDays = datetime.minusDays(n);
        return Date.from(minusDays.toInstant());
    }
    
    public static Date getAfterDay(Date day, int n) {
    	return getBeforeDay(day, -n);
    }

    // 01. java.util.Date --> java.time.LocalDateTime
    public static LocalDateTime UDateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    // 02. java.util.Date --> java.time.LocalDate
    public static LocalDate UDateToLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    // 03. java.util.Date --> java.time.LocalTime
    public static LocalTime UDateToLocalTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }


    // 04. java.time.LocalDateTime --> java.util.Date
    public static Date LocalDateTimeToUdate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }


    // 05. java.time.LocalDate --> java.util.Date
    public static Date LocalDateToUdate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    // 06. java.time.LocalTime --> java.util.Date
    public static Date LocalTimeToUdate(LocalDate localDate, LocalTime localTime) {
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }
    
    public static long getDayZero(long time) {
    	Calendar c = Calendar.getInstance();
    	c.setTimeInMillis(time);
    	c.set(Calendar.HOUR_OF_DAY,0);
    	c.set(Calendar.MINUTE,0);
    	c.set(Calendar.SECOND,0);
    	c.set(Calendar.MILLISECOND,0);
    	return c.getTimeInMillis();
    }
}
